index.page.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import type { ReactNode, JSX } from 'react';
  2. import React, { useEffect } from 'react';
  3. import type {
  4. GetServerSideProps, GetServerSidePropsContext,
  5. } from 'next';
  6. import dynamic from 'next/dynamic';
  7. import Head from 'next/head';
  8. import { ShareLinkLayout } from '~/components/Layout/ShareLinkLayout';
  9. import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
  10. import { ShareLinkPageView } from '~/components/ShareLinkPageView';
  11. import type { CommonEachProps } from '~/pages/common-props';
  12. import { NextjsRoutingType, detectNextjsRoutingType } from '~/pages/utils/nextjs-routing-utils';
  13. import { useCustomTitleForPage } from '~/pages/utils/page-title-customization';
  14. import { useIsSearchPage, useIsSharedUser } from '~/states/context';
  15. import {
  16. useCurrentPageData, useCurrentPagePath,
  17. } from '~/states/page';
  18. import { useHydratePageAtoms } from '~/states/page/hydrate';
  19. import { useDisableLinkSharing, useRendererConfig } from '~/states/server-configurations';
  20. import loggerFactory from '~/utils/logger';
  21. import type { NextPageWithLayout } from '../../_app.page';
  22. import type { GeneralPageInitialProps } from '../../general-page';
  23. import { useInitialCSRFetch } from '../../general-page';
  24. import { registerPageToShowRevisionWithMeta } from '../../general-page/superjson';
  25. import { useHydrateServerConfigurationAtoms } from '../../general-page/use-hydrate-server-configurations';
  26. import { NEXT_JS_ROUTING_PAGE } from './consts';
  27. import { getServerSidePropsForInitial, getServerSidePropsForSameRoute } from './server-side-props';
  28. import type { ShareLinkInitialProps } from './types';
  29. // call superjson custom register
  30. registerPageToShowRevisionWithMeta();
  31. const GrowiContextualSubNavigation = dynamic(() => import('~/client/components/Navbar/GrowiContextualSubNavigation'), { ssr: false });
  32. const logger = loggerFactory('growi:next-page:share');
  33. type InitialProps = CommonEachProps & GeneralPageInitialProps & ShareLinkInitialProps;
  34. type Props = CommonEachProps | InitialProps;
  35. const isInitialProps = (props: Props): props is InitialProps => {
  36. return 'isNextjsRoutingTypeInitial' in props && props.isNextjsRoutingTypeInitial;
  37. };
  38. const SharedPage: NextPageWithLayout<Props> = (props: Props) => {
  39. // Initialize Jotai atoms with initial data - must be called unconditionally
  40. const shareLink = isInitialProps(props) ? props.shareLink : undefined;
  41. const isExpired = isInitialProps(props) ? props.isExpired : undefined;
  42. const pageData = isInitialProps(props) ? props.pageWithMeta?.data : undefined;
  43. useHydratePageAtoms(pageData, {
  44. shareLinkId: shareLink?._id,
  45. });
  46. const [currentPage] = useCurrentPageData();
  47. const [currentPagePath] = useCurrentPagePath();
  48. const [rendererConfig] = useRendererConfig();
  49. const [, setIsSharedUser] = useIsSharedUser();
  50. const [, setIsSearchPage] = useIsSearchPage();
  51. const [isLinkSharingDisabled] = useDisableLinkSharing();
  52. // Use custom hooks for navigation and routing
  53. // useSameRouteNavigation();
  54. // If initial props and skipSSR, fetch page data on client-side
  55. useInitialCSRFetch(isInitialProps(props) && props.skipSSR);
  56. // Initialize atom values
  57. useEffect(() => {
  58. setIsSharedUser(true);
  59. setIsSearchPage(false);
  60. }, [setIsSharedUser, setIsSearchPage]);
  61. // If the data on the page changes without router.push, pageWithMeta remains old because getServerSideProps() is not executed
  62. // So preferentially take page data from useSWRxCurrentPage
  63. const pagePath = currentPagePath ?? props.currentPathname;
  64. const title = useCustomTitleForPage(pagePath);
  65. return (
  66. <>
  67. <Head>
  68. <title>{title}</title>
  69. </Head>
  70. <div className="dynamic-layout-root justify-content-between">
  71. <GrowiContextualSubNavigation currentPage={currentPage} />
  72. <ShareLinkPageView
  73. pagePath={pagePath}
  74. rendererConfig={rendererConfig}
  75. shareLink={shareLink}
  76. isExpired={isExpired}
  77. disableLinkSharing={isLinkSharingDisabled}
  78. />
  79. </div>
  80. </>
  81. );
  82. };
  83. type LayoutProps = Props & {
  84. children?: ReactNode
  85. }
  86. const Layout = ({ children, ...props }: LayoutProps): JSX.Element => {
  87. // Hydrate sidebar atoms with server-side data - must be called unconditionally
  88. const initialProps = isInitialProps(props) ? props : undefined;
  89. useHydrateServerConfigurationAtoms(initialProps?.serverConfig, initialProps?.rendererConfig);
  90. return <ShareLinkLayout>{children}</ShareLinkLayout>;
  91. };
  92. SharedPage.getLayout = function getLayout(page) {
  93. return (
  94. <>
  95. <DrawioViewerScript drawioUri={page.props.rendererConfig.drawioUri} />
  96. <Layout {...page.props}>
  97. {page}
  98. </Layout>
  99. </>
  100. );
  101. };
  102. // function getAction(props: Props): SupportedActionType {
  103. // let action: SupportedActionType;
  104. // if (props.isExpired) {
  105. // action = SupportedAction.ACTION_SHARE_LINK_EXPIRED_PAGE_VIEW;
  106. // }
  107. // else if (props.shareLink == null) {
  108. // action = SupportedAction.ACTION_SHARE_LINK_NOT_FOUND;
  109. // }
  110. // else {
  111. // action = SupportedAction.ACTION_SHARE_LINK_PAGE_VIEW;
  112. // }
  113. // return action;
  114. // }
  115. export const getServerSideProps: GetServerSideProps<Props> = async(context: GetServerSidePropsContext) => {
  116. // detect Next.js routing type
  117. const nextjsRoutingType = detectNextjsRoutingType(context, NEXT_JS_ROUTING_PAGE);
  118. if (nextjsRoutingType === NextjsRoutingType.INITIAL) {
  119. return getServerSidePropsForInitial(context);
  120. }
  121. // Lightweight props for same-route navigation
  122. return getServerSidePropsForSameRoute(context);
  123. };
  124. export default SharedPage;